package com.danbay.framework.protocol.command;


import com.danbay.framework.exception.CodeBusinessException;
import com.danbay.framework.exception.IExceptionData;
import com.danbay.framework.exception.MessageCodeException;
import com.danbay.framework.protocol.command.codec.ICmdDataDecoder;
import com.danbay.framework.protocol.command.codec.ICmdDataEncoder;
import com.danbay.framework.utils.ExceptionUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;

/**
 * @author SJG
 */
public abstract class AbstractCommandHandler implements ICommandHandler {
    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private CommandBuilder commandBuilder;
    @Autowired(required = false)
    private ICommandSuccessNotifier successNotifier;
    @Autowired(required = false)
    private ICommandFailNotifier failNotifier;

    /**
     * 是否暴露异常信息
     */
    private Boolean exposeExceptionMsg;

    @Override
    public CommandAction getCommand(String route) {
        return commandBuilder.getCommand(route);
    }

    /**
     * 初始化后要做的事
     */
    public abstract void init();

    @Override
    public Boolean isExposeExceptionMsg() {
        if (exposeExceptionMsg == null) {
            return false;
        }
        return this.exposeExceptionMsg;
    }

    @Override
    public Object invokeCmd(ICommandProtocol msg, ICommandSelector selector, ICmdDataDecoder decoder, ICmdDataEncoder encoder) throws MessageCodeException {

        //默认使用消息名作
        String cmdPath = msg.getCmdName().trim();
        if (selector != null) {
            cmdPath = selector.select(msg);
        }

        //取可执行命令对象
        CommandAction action = commandBuilder.getCommand(cmdPath);
        if (action == null) {
            logger.info(msg.getCmdName().trim());
            throw new MessageCodeException("命令方法调用失败,命令\"" + cmdPath + "\"不存在,错误码：1000", 1000);
        }

        Object[] args;
        Object dataObj = args = new Object[0];
        Type type = action.getParamType();
        //有参数
        if (type != null && msg.getData() != null) {
            dataObj = decoder.decode(msg.getData(), action.getParamType());
            if (dataObj != null) {
                args = new Object[]{dataObj};
            }
        }

        String objJson = "see above";
        //debug模式输出参数值json
        if (logger.isDebugEnabled()) {
            try {
                objJson = new ObjectMapper().writeValueAsString(dataObj);
            } catch (JsonProcessingException e1) {
                logger.error("start invoke", e1);
            }
        }

        logger.info("start invoke command===\n route:" + action.getRoute() + ", arg type:" + (type == null ? "void" : type.getTypeName()) + ", arg value:" + objJson);

        Method m = action.getAction();
        //调用命令方法
        try {
            Object result = m.invoke(action.getCommand(), args);
            //如果执行成功,则发起成功通知
            if (successNotifier != null) {
                successNotifier.notifySuccess(cmdPath, dataObj, result);
            }
            return result;
        } catch (InvocationTargetException ite) {
            //如果执行失败,则发起失败通知
            if (failNotifier != null) {
                failNotifier.notifyFail(cmdPath, dataObj, ite.getMessage());
            }
            Throwable targetException = ite.getTargetException();
            //业务异常
            if (targetException instanceof CodeBusinessException) {
                //普通业务异常
                CodeBusinessException cbe = (CodeBusinessException) ite.getTargetException();
                MessageCodeException mce = new MessageCodeException(cbe.getMessage(), cbe.getCode(), true, ite);
                //包含异常数据的异常
                if (targetException instanceof IExceptionData) {
                    //取出数据
                    IExceptionData ed = (IExceptionData) targetException;
                    mce.setData(ed.getData());
                }
                throw mce;
            }
            //错误码0001:命令方法调用失败
            throw new MessageCodeException("命令方法调用失败,错误码：1000. " + ExceptionUtils.getExceptionMessage(ite), 1000, ite);
        } catch (Exception e) {
            //如果执行失败,则发起失败通知
            if (failNotifier != null) {
                failNotifier.notifyFail(cmdPath, dataObj, e.getMessage());
            }
            if (e instanceof MessageCodeException) {
                throw (MessageCodeException) e;
            }
            //错误码0001:命令方法调用失败
            throw new MessageCodeException("命令方法调用失败,错误码：1000. " + ExceptionUtils.getExceptionMessage(e), 1000, e);
        }
    }

    public void setExposeExceptionMsg(Boolean exposeExceptionMsg) {
        this.exposeExceptionMsg = exposeExceptionMsg;
    }


}
